﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace SilverlightContrib.Controls
{
    /// <summary>
    /// Represents a Color Picker control which allows a user to select a color.
    /// </summary>
    public class ColorPicker : Control
    {
        public delegate void ColorSelectedHandler(Color c);

        /// <summary>
        /// Event fired when a color is selected.
        /// </summary>
        public event ColorSelectedHandler ColorSelected;
        
        private readonly ColorSpace m_colorSpace;
        private bool m_hueMonitorMouseCaptured;
        private bool m_sampleMouseCaptured;
        private double m_huePos;
        private double m_sampleX;
        private double m_sampleY;
        
        private Rectangle m_hueMonitor;
        private Canvas m_sampleSelector;
        private Canvas m_hueSelector;

        private Rectangle m_selectedColorView;
        private Rectangle m_colorSample;
        private TextBlock m_hexValue;

        public ColorPicker()
        {
            DefaultStyleKey = typeof(ColorPicker);
            m_colorSpace = new ColorSpace();
            this.LayoutUpdated += new EventHandler(ColorPicker_LayoutUpdated);
        }

        private void ColorPicker_LayoutUpdated(object sender, EventArgs e)
        {
            UpdateVisuals();
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            m_hueMonitor = GetTemplateChild("HueMonitor") as Rectangle;
            m_sampleSelector = GetTemplateChild("SampleSelector") as Canvas;
            m_hueSelector = GetTemplateChild("HueSelector") as Canvas;
            m_selectedColorView = GetTemplateChild("SelectedColorView") as Rectangle;
            m_colorSample = GetTemplateChild("ColorSample") as Rectangle;
            m_hexValue = GetTemplateChild("HexValue") as TextBlock;


            m_hueMonitor.MouseLeftButtonDown += new MouseButtonEventHandler(rectHueMonitor_MouseLeftButtonDown);
            m_hueMonitor.MouseLeftButtonUp += new MouseButtonEventHandler(rectHueMonitor_MouseLeftButtonUp);
            m_hueMonitor.MouseMove += new MouseEventHandler(rectHueMonitor_MouseMove);

            m_colorSample.MouseLeftButtonDown += new MouseButtonEventHandler(rectSampleMonitor_MouseLeftButtonDown);
            m_colorSample.MouseLeftButtonUp += new MouseButtonEventHandler(rectSampleMonitor_MouseLeftButtonUp);
            m_colorSample.MouseMove += new MouseEventHandler(rectSampleMonitor_MouseMove);

            m_sampleX = m_colorSample.Width;
            m_sampleY = 0;
            m_huePos = 0;
        
            UpdateSatValSelection();
        }

        private void rectHueMonitor_MouseLeftButtonDown(object sender, MouseEventArgs e)
        {

            m_hueMonitorMouseCaptured = m_hueMonitor.CaptureMouse();
            DragSliders(0, e.GetPosition((UIElement)sender).Y);
        }

        private void rectHueMonitor_MouseLeftButtonUp(object sender, MouseEventArgs e)
        {
            m_hueMonitor.ReleaseMouseCapture();
            m_hueMonitorMouseCaptured = false;
            SetValue(SelectedColorProperty, GetColor());
        }

        private void rectHueMonitor_MouseMove(object sender, MouseEventArgs e)
        {
            DragSliders(0, e.GetPosition((UIElement) sender).Y);
        }

        private void rectSampleMonitor_MouseLeftButtonDown(object sender, MouseEventArgs e)
        {           
            m_sampleMouseCaptured = m_colorSample.CaptureMouse();
            Point pos = e.GetPosition((UIElement)sender);
            DragSliders(pos.X, pos.Y);
        }

        private void rectSampleMonitor_MouseLeftButtonUp(object sender, MouseEventArgs e)
        {
            m_colorSample.ReleaseMouseCapture();
            m_sampleMouseCaptured = false;
            SetValue(SelectedColorProperty, GetColor());
        }

        private void rectSampleMonitor_MouseMove(object sender, MouseEventArgs e)
        {
            if(!m_sampleMouseCaptured)
                return;

            Point pos = e.GetPosition((UIElement)sender);
            DragSliders(pos.X, pos.Y);
        }


        private Color GetColor()
        {
            double yComponent = 1 - (m_sampleY / m_colorSample.Height);
            double xComponent = m_sampleX / m_colorSample.Width;
            double hueComponent = (m_huePos / m_hueMonitor.Height) * 360;

            return m_colorSpace.ConvertHsvToRgb(hueComponent, xComponent, yComponent);
        }

        private void UpdateSatValSelection()
        {
            if (m_colorSample == null)
                return;

            m_sampleSelector.SetValue(Canvas.LeftProperty, m_sampleX - (m_sampleSelector.Height / 2));
            m_sampleSelector.SetValue(Canvas.TopProperty, m_sampleY - (m_sampleSelector.Height / 2));

            Color currColor = GetColor();
            m_selectedColorView.Fill = new SolidColorBrush(currColor);
            m_hexValue.Text = m_colorSpace.GetHexCode(currColor);
        }

        private void UpdateHueSelection()
        {
            if (m_hueMonitor == null)
                return;
            double huePos = m_huePos / m_hueMonitor.Height * 255;
            Color c = m_colorSpace.GetColorFromPosition(huePos);
            m_colorSample.Fill = new SolidColorBrush(c);

            m_hueSelector.SetValue(Canvas.TopProperty, m_huePos - (m_hueSelector.Height / 2));

            Color currColor = GetColor();
            
            m_selectedColorView.Fill = new SolidColorBrush(currColor);
            m_hexValue.Text = m_colorSpace.GetHexCode(currColor);
        }

        private void UpdateVisuals()
        {
            if (m_hueMonitor == null)
                return;

            Color c = this.SelectedColor;
            ColorSpace cs = new ColorSpace();
            HSV hsv = cs.ConvertRgbToHsv(c);

            m_huePos = (hsv.Hue/360*m_hueMonitor.Height);
            m_sampleY = -1*(hsv.Value-1)*m_colorSample.Height;
            m_sampleX = hsv.Saturation*m_colorSample.Width;
            if(!double.IsNaN(m_huePos))
                UpdateHueSelection();
            UpdateSatValSelection();
        }

        private void DragSliders(double x, double y)
        {
            if(m_hueMonitorMouseCaptured)
            {
                if (y < 0)
                    m_huePos = 0;
                else if (y > m_hueMonitor.Height)
                    m_huePos = m_hueMonitor.Height;
                else
                    m_huePos = y;
                UpdateHueSelection();
            }
            else if(m_sampleMouseCaptured)
            {
                if (x < 0)
                    m_sampleX = 0;
                else if (x > m_colorSample.Width)
                    m_sampleX = m_colorSample.Width;
                else
                    m_sampleX = x;

                if (y < 0)
                    m_sampleY = 0;
                else if (y > m_colorSample.Height)
                    m_sampleY = m_colorSample.Height;
                else
                    m_sampleY = y;

                UpdateSatValSelection();                
            }
        }

        #region SelectedColor Dependency Property
        /// <summary>
        /// Gets or sets the currently selected color in the Color Picker.
        /// </summary>
        public Color SelectedColor
        {
            get { return (Color) GetValue(SelectedColorProperty);}
            set
            {
                SetValue(SelectedColorProperty, value);
                this.UpdateVisuals();
            }
        }

        /// <summary>
        /// SelectedColor Dependency Property.
        /// </summary>
        public static readonly DependencyProperty SelectedColorProperty =
            DependencyProperty.Register(
                "SelectedColor",
                typeof(Color),
                typeof(ColorPicker),
                new PropertyMetadata(new PropertyChangedCallback(SelectedColorChanged)));

        private static void SelectedColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ColorPicker p = d as ColorPicker;
            if (p != null && p.ColorSelected != null)
            {
                p.ColorSelected((Color) e.NewValue);
            }
        }


        #endregion

    }
}
